const fs = require("fs");
const path = require("path");
const { parse } = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const babel = require("@babel/core");

// 分析入口文件
const moduleAnalyse = (filename) => {
  // 读取本地入口文件
  const content = fs.readFileSync(filename, "utf-8");
  // 通过babel-parser将源代码转换为AST语法树
  const ast = parse(content, {
    sourceType: "module",
  });
  // 利用babel-traverse遍历AST语法树，获取文件依赖
  const dependencies = {};
  traverse(ast, {
    ImportDeclaration({ node }) {
      const dirname = path.dirname(filename);
      const file = "./" + path.join(dirname, node.source.value);
      dependencies[node.source.value] = file;
    },
  });
  // 通过babel-core将es6语法的代码转换为es5
  const { code } = babel.transformFromAst(ast, null, {
    presets: ["@babel/preset-env"],
  });
  // 导出分析结果
  return {
    filename,
    dependencies,
    code,
  };
};

// 利用队列遍历所有依赖，生成依赖图谱
const makeDependenciesGraph = (entry) => {
  const entryModule = moduleAnalyse(entry);
  const graphArray = [entryModule];
  for (let i = 0; i < graphArray.length; i++) {
    const { dependencies } = graphArray[i];
    if (dependencies) {
      for (const key in dependencies) {
        graphArray.push(moduleAnalyse(dependencies[key]));
      }
    }
  }
  const graph = {};
  graphArray.forEach((item) => {
    graph[item.filename] = {
      dependencies: item.dependencies,
      code: item.code,
    };
  });
  return graph;
};

const generateCode = (entry) => {
  const graphInfo = JSON.stringify(makeDependenciesGraph(entry));
  return `
  (function(graphInfo){
    function require(module){
        var exports = {}
        function localRequire(relatePath){
            return require(graphInfo[module].dependencies[relatePath])
        }
        (function(require,exports,code){
            eval(code)
        })(localRequire,exports,graphInfo[module].code)
        return exports
    };
    require('${entry}')
  })(${graphInfo})
  `;
};

const code = generateCode("./src/index.js");
fs.writeFileSync(path.resolve(__dirname, "dist/main.js"), code);
